﻿#region Using

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

#endregion

namespace XNgine.Input
{
    /// <summary>
    /// An input device that keeps track of the GamePad and GamePadState
    /// </summary>
    public class GamepadDevice : InputDevice<GamePadState>
    {
        /// <summary>
        /// GamePad state during the prevous update
        /// </summary>
        private GamePadState last;

        /// <summary>
        /// GamePad state during the current update
        /// </summary>
        private GamePadState current;

        /// <summary>
        /// Buttons that are currently down
        /// </summary>
        private Buttons[] currentButtons;

        /// <summary>
        /// Current GamePad state
        /// </summary>
        public override GamePadState State
        {
            get { return current; }
        }

        /// <summary>
        /// Currently pressed buttons
        /// </summary>
        public Buttons[] PressedButtons
        {
            get { return currentButtons; }
        }

        /// <summary>
        /// The position in (X,Y) coordinates of the left thumbstick
        /// </summary>
        public Vector2 LeftStickPosition
        {
            get { return current.ThumbSticks.Left; }
        }

        /// <summary>
        /// The position in (X,Y) coordinates of the right thumbstick
        /// </summary>
        public Vector2 RightStickPosition
        {
            get { return current.ThumbSticks.Right; }
        }

        /// <summary>
        /// The position of the left trigger
        /// </summary>
        public float LeftTrigger
        {
            get { return current.Triggers.Left; }
        }

        /// <summary>
        /// The position of the right trigger
        /// </summary>
        public float RightTrigger
        {
            get { return current.Triggers.Right; }
        }

        public Vector2 _leftStickDelta = Vector2.Zero;

        /// <summary>
        /// The change in position of the left thumbstrick between the last two frames
        /// </summary>
        public Vector2 LeftStickDelta
        {
            get { return _leftStickDelta; }
        }

        public Vector2 _rightStickDelta = Vector2.Zero;

        /// <summary>
        /// The change in position of the right thumbstrick between the last two frames
        /// </summary>
        public Vector2 RightStickDelta
        {
            get { return _rightStickDelta; }
        }

        public float _leftTriggerDelta;

        /// <summary>
        /// The change in position of the right trigger between the last two frames
        /// </summary>
        public float LeftTriggerDelta
        {
            get { return _leftTriggerDelta; }
        }

        public float _rightTriggerDelta;

        /// <summary>
        /// The change in position of the right trigger between the last two frames
        /// </summary>
        public float RightTriggerDelta
        {
            get { return _rightTriggerDelta; }
        }

        /// <summary>
        /// The GamePad's PlayerIndex
        /// </summary>
        public PlayerIndex PlayerIndex { get; set; }

        /// <summary>
        /// Whether the controller is connected
        /// </summary>
        public bool IsConnected
        {
            get { return current.IsConnected; }
        }

        /// <summary>
        /// This event is fired when a button is newly pressed
        /// </summary>
        public event InputEventHandler<Buttons, GamePadState> ButtonPressed;

        /// <summary>
        /// This event is fired when a button is just released
        /// </summary>
        public event InputEventHandler<Buttons, GamePadState> ButtonReleased;

        /// <summary>
        /// This event is fired when a button is held (it is fired in every Update call)
        /// </summary>
        public event InputEventHandler<Buttons, GamePadState> ButtonHeld;

        /// <summary>
        /// This event is fired when the controller is connected
        /// </summary>
        public event EventHandler Connected;

        /// <summary>
        /// This event is fired when the controller is disconnected
        /// </summary>
        public event EventHandler Disconnected;

        /// <summary>
        /// Constructor gets the initial GamePadState, and does the first update.
        /// </summary>
        /// <param name="PlayerIndex"> PlayerIndex of the controller (One, Two, Three, Four)</param>
        public GamepadDevice(PlayerIndex PlayerIndex)
        {
            this.PlayerIndex = PlayerIndex;
            current = GamePad.GetState(PlayerIndex);
            Update();
        }

        /// <summary>
        /// List of pressed buttons used to setup currentButtons
        /// </summary>
        private readonly List<Buttons> pressed = new List<Buttons>();

        /// <summary>
        /// Performs the update of the GamePad state (once every frame)
        /// </summary>
        public override void Update()
        {
            // Update the last state
            last = current;

            // Update the current state and clear the list of pressed
            // buttons
            current = GamePad.GetState(PlayerIndex);
            pressed.Clear();

            // Only update if the controller is still connected
            if (IsConnected)
            {
                // If the controller connected this frame, fire the event
                if (!last.IsConnected)
                    if (Connected != null)
                        Connected(this, new EventArgs());

                _leftStickDelta = current.ThumbSticks.Left - last.ThumbSticks.Left;
                _rightStickDelta = current.ThumbSticks.Right - last.ThumbSticks.Right;
                _leftTriggerDelta = current.Triggers.Left - last.Triggers.Left;
                _rightTriggerDelta = current.Triggers.Right - last.Triggers.Right;

                // For each mouse button...
                foreach (Buttons button in Enum.GetValues(typeof (Buttons)))
                {
                    // If it is down, add it to the list
                    if (IsButtonDown(button))
                        pressed.Add(button);

                    // If it was just pressed, fire the event
                    if (WasButtonPressed(button))
                        if (ButtonPressed != null)
                            ButtonPressed(this, new InputDeviceEventArgs
                                                    <Buttons, GamePadState>(button, this));

                    // If it was just released, fire the event
                    if (WasButtonReleased(button))
                        if (ButtonReleased != null)
                            ButtonReleased(this, new InputDeviceEventArgs
                                                     <Buttons, GamePadState>(button, this));

                    // If it was held, fire the event
                    if (WasButtonHeld(button))
                        if (ButtonHeld != null)
                            ButtonHeld(this, new InputDeviceEventArgs
                                                 <Buttons, GamePadState>(button, this));
                }

                // Update the currentButtons array from the list of buttons
                // that are down
                currentButtons = pressed.ToArray();
            }
            else
                // If the controller was just disconnected, fire the event
                if (last.IsConnected)
                    if (Disconnected != null)
                        Disconnected(this, new EventArgs());
        }

        /// <summary>
        /// Cheks if the specified button is currently down
        /// </summary>
        /// <param name="button">The button to be checked</param>
        public bool IsButtonDown(Buttons button)
        {
            return current.IsButtonDown(button);
        }

        /// <summary>
        /// Cheks if the specified button is currently up
        /// </summary>
        /// <param name="button">The button to be checked</param>
        public bool IsButtonUp(Buttons button)
        {
            return current.IsButtonUp(button);
        }

        /// <summary>
        /// Cheks if the specified button is down for the first time this frame
        /// </summary>
        /// <param name="button">The button to be checked</param>
        public bool WasButtonPressed(Buttons button)
        {
            if (last.IsButtonUp(button) && current.IsButtonDown(button))
                return true;

            return false;
        }

        /// <summary>
        /// Cheks if the specified button is up for the first time this frame
        /// </summary>
        /// <param name="button">The button to be checked</param>
        public bool WasButtonReleased(Buttons button)
        {
            if (last.IsButtonDown(button) && current.IsButtonUp(button))
                return true;

            return false;
        }

        /// <summary>
        /// Checks if the specified button has been down for more than one frame
        /// </summary>
        /// <param name="button">The button to be checked</param>
        public bool WasButtonHeld(Buttons button)
        {
            if (last.IsButtonDown(button) && current.IsButtonDown(button))
                return true;

            return false;
        }
    }
}